#include "Arduino.h"    
#include <OneButton.h>       
#include <WiFi.h>
#include <sddc.h>
#include <cJSON.h>
#include <Wire.h>
#include <Adafruit_MLX90614.h>

#define SDDC_CFG_PORT             680U             // SDDC 协议使用的端口号
#define CAMERA_MODEL_AI_THINKER                    // 安信可 esp-32 有 PSRAM
#define PIN_INPUT 0                                // 选择 IO0 进行控制
#define ESP_TASK_STACK_SIZE   4096
#define ESP_TASK_PRIO         25

static const int ledPin = 13;                      // 输出引脚
static const int sensor_in = A0;                   // 数据输入引脚
static sddc_t *g_sddc;
int    g_flag = SDDC_FALSE;
static const char* ssid     = "EOS-000041";        // WiFi 名
static const char* password = "1234567890";        // WiFi 密码

OneButton button(PIN_INPUT, true);

static void esp_sensor_task(void *arg)
{  
    cJSON *value;
    value = cJSON_CreateObject();
    sddc_return_if_fail(value);
    char  *msg;
      
    while(g_flag == SDDC_TRUE)
    {
        value = cJSON_CreateObject();
        sddc_return_if_fail(value);
        
        // 获取传感器数据
        if(digitalRead(sensor_in)==LOW)   //Read Touch sensor signal
        {      
            digitalWrite(ledPin, HIGH);    // if Touch sensor is LOW, then turn on
            digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
        }else{
            digitalWrite(ledPin, LOW);     // if Touch sensor is HIGH, then turn off the led
            digitalWrite(LED_BUILTIN, LOW);   // turn the LED off by making the voltage LOW
        }
        cJSON_AddNumberToObject(value, "Turbidity data", sensorValue);
        
        // 发送数据给 EdgerOS
        msg = cJSON_Print(value);
        if (sddc_broadcast_message(g_sddc, msg, strlen(msg), 1, SDDC_FALSE, NULL) != 0)
        {
            sddc_printf("send msg error!\n");
            break;
        }
        cJSON_Delete(value);
        delay(100);
    }       
    // 通知 EdgerOS 已停止发送数据
    Serial.printf("Soil humidity data OFF\n");

    cJSON_free(msg);
    
    vTaskDelete(NULL);
}
/*
 * 事件响应函数
 */
static sddc_bool_t iot_pi_on_message(sddc_t *sddc, const uint8_t *uid, const char *message, size_t len)
{
    cJSON *root = cJSON_Parse(message);

    static cJSON *item;
    char  *str;

    sddc_return_value_if_fail(root, SDDC_TRUE);

    // 判断收到的是否是水质浊度传感器命令
    if((item = cJSON_GetObjectItem(root, "Turbidity sensor")) != NULL)
    {
        sddc_return_value_if_fail(item, SDDC_TRUE);

        if((strcmp(item->valuestring, "OFF")) == 0)
        {
            g_flag = SDDC_FALSE;
        }
        if ((strcmp(item->valuestring, "ON")) == 0)
        {
            g_flag = SDDC_TRUE;
        }
        // 创建传感器任务，获取水质浊度传感器的数据并发送给 EdgerOS
        xTaskCreate(esp_sensor_task, "sensor_tick", ESP_TASK_STACK_SIZE, NULL, ESP_TASK_PRIO, NULL);
    }else
    {
        sddc_printf("Unknown command ！\n");
    }
    
error:
    cJSON_Delete(root);

    return SDDC_TRUE;
}
/*
 * 接收消息请求时的回调函数
 */
static void iot_pi_on_message_ack(sddc_t *sddc, const uint8_t *uid, uint16_t seqno)
{
}
/*
 * 丢失消息时的回调函数
 */
static void iot_pi_on_message_lost(sddc_t *sddc, const uint8_t *uid, uint16_t seqno)
{
}
/*
 * EdgerOS 断连时的回调函数
 */
static void iot_pi_on_edgeros_lost(sddc_t *sddc, const uint8_t *uid)
{
}
/*
 * 接收更新请求时的回调函数
 */
static sddc_bool_t iot_pi_on_update(sddc_t *sddc, const uint8_t *uid, const char *udpate_data, size_t len)
{
    cJSON *root = cJSON_Parse(udpate_data);
    char *str;

    sddc_return_value_if_fail(root, SDDC_FALSE);

    str = cJSON_Print(root);
    sddc_goto_error_if_fail(str);

    sddc_printf("iot_pi_on_update: %s\n", str);
    cJSON_free(str);

    cJSON_Delete(root);

    return SDDC_TRUE;

error:
    cJSON_Delete(root);

    return SDDC_FALSE;
}
/*
 * 接受邀请请求时的回调函数
 */
static sddc_bool_t iot_pi_on_invite(sddc_t *sddc, const uint8_t *uid, const char *invite_data, size_t len)
{
    cJSON *root = cJSON_Parse(invite_data);
    char *str;

    sddc_return_value_if_fail(root, SDDC_FALSE);

    str = cJSON_Print(root);
    sddc_goto_error_if_fail(str);
    
    sddc_printf("iot_pi_on_invite: %s\n", str);
    cJSON_free(str);

    cJSON_Delete(root);

    return SDDC_TRUE;

error:
    cJSON_Delete(root);

    return SDDC_FALSE;
}
/*
 * 发送邀请后的回调函数
 */
static sddc_bool_t iot_pi_on_invite_end(sddc_t *sddc, const uint8_t *uid)
{
    return SDDC_TRUE;
}
/*
 * 创建 REPORT 数据
 */
static char *iot_pi_report_data_create(void)
{
    cJSON *root;
    cJSON *report;
    char *str;

    root = cJSON_CreateObject();
    sddc_return_value_if_fail(root, NULL);

    report = cJSON_CreateObject();
    sddc_return_value_if_fail(report, NULL);

    cJSON_AddItemToObject(root, "report", report);
    cJSON_AddStringToObject(report, "name",   "IoT Pi");
    cJSON_AddStringToObject(report, "type",   "device");
    cJSON_AddBoolToObject(report,   "excl",   SDDC_FALSE);
    cJSON_AddStringToObject(report, "desc",   "翼 辉 IoT Pi");
    cJSON_AddStringToObject(report, "model",  "1");
    cJSON_AddStringToObject(report, "vendor", "ACOINFO");
    
    str = cJSON_Print(root);
    sddc_return_value_if_fail(str, NULL);
    sddc_printf("REPORT DATA: %s\n", str);
    cJSON_Delete(root);

    return str;
}
/*
 * 创建 INVITE 数据
 */
static char *iot_pi_invite_data_create(void)
{
    cJSON *root;
    cJSON *report;
    char *str;

    root = cJSON_CreateObject();
    sddc_return_value_if_fail(root, NULL);

    report = cJSON_CreateObject();
    sddc_return_value_if_fail(report, NULL);

    cJSON_AddItemToObject(root, "report", report);
    cJSON_AddStringToObject(report, "name",   "IoT Pi");
    cJSON_AddStringToObject(report, "type",   "device");
    cJSON_AddBoolToObject(report,   "excl",   SDDC_FALSE);
    cJSON_AddStringToObject(report, "desc",   "翼辉 IoT Pi");
    cJSON_AddStringToObject(report, "model",  "1");
    cJSON_AddStringToObject(report, "vendor", "ACOINFO");

    str = cJSON_Print(root);
    sddc_return_value_if_fail(str, NULL);

    sddc_printf("INVITE DATA: %s\n", str);
    
    cJSON_Delete(root);

    return str;
}
/*
 * IO0 按键检测任务
 */
static void esp_io0_key_task()
{
    WiFi.disconnect();
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(1000);
        Serial.println("WIFI Wait for Smartconfig");
        
        // 设置为Station模式
        WiFi.mode(WIFI_STA);
        
        // 开始智能配网
        WiFi.beginSmartConfig();
        while (1)
        {
            delay(500);
            // 检查智能配网
            if (WiFi.smartConfigDone())
            {
                // 设置自动连接
                WiFi.setAutoConnect(true);  
                break;
            }
        }
    }
}
/*
 * 循环扫描按键
 */
static void esp_tick_task(void *arg)
{
    void *sddc = arg;
    (void)sddc;
    while(1)
    {
        button.tick();
        delay(100);
    }
}

void setup() {
    byte mac[6];
    char *data;
    int ret;
    
    // 初始化串口
    Serial.begin(115200);
    Serial.setDebugOutput(true);
    Serial.println();
    
    // 配置引脚
    pinMode(LED_BUILTIN, OUTPUT);
    pinMode(ledPin, OUTPUT);      // Set ledPin as output mode
    pinMode(sensor_in, INPUT);       //Set Turbidity sensor pin to input mode
     
    // 清除一下按键状态机的状态
    button.reset();
    
    // 创建按键扫描线程，长按 IO0 按键，松开后ESP32 将会进入 SmartConfig 模式
    sddc_printf("长按按键进入 Smartconfig...\n");
    button.attachLongPressStop(esp_io0_key_task);
    xTaskCreate(esp_tick_task, "button_tick", ESP_TASK_STACK_SIZE, NULL, ESP_TASK_PRIO, NULL);
    
    // 启动 WiFi 并且连接网络
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) 
    {
      delay(500);
      Serial.print(".");
    }
  
    // 获取并打印 IP 地址
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.print("'ip :");
    Serial.print(WiFi.localIP());
    Serial.println("' to connect");   
    
    // 创建 SDDC 协议对象
    g_sddc = sddc_create(SDDC_CFG_PORT);
    
    // 设置事件响应函数
    sddc_set_on_message(g_sddc, iot_pi_on_message);                              // 设置接收消息请求时的回调函数
    sddc_set_on_message_ack(g_sddc, iot_pi_on_message_ack);                      // 设置接收消息确认时的回调函数
    sddc_set_on_message_lost(g_sddc, iot_pi_on_message_lost);                    // 设置丢失消息时的回调函数
    sddc_set_on_invite(g_sddc, iot_pi_on_invite);                                // 设置接受邀请请求时的回调函数
    sddc_set_on_invite_end(g_sddc, iot_pi_on_invite_end);                        // 设置发送邀请后的回调函数
    sddc_set_on_update(g_sddc, iot_pi_on_update);                                // 设置接收更新请求时的回调函数
    sddc_set_on_edgeros_lost(g_sddc, iot_pi_on_edgeros_lost);                    // 设置 EdgerOS 断连时的回调函数

  // 设置设备密码
#if SDDC_CFG_SECURITY_EN > 0                                                   // SDDC_CFG_SECURITY_EN 宏控制是否支持数据加密通信
    ret = sddc_set_token(g_sddc, "1234567890");
#endif 

    // 创建并设置 Report 报文数据
    data = iot_pi_report_data_create();
    sddc_return_if_fail(data);
    sddc_set_report_data(g_sddc, data, strlen(data));
    
    // 创建并设置 Invite 报文数据
    data = iot_pi_invite_data_create();
    sddc_return_if_fail(data);
    sddc_set_invite_data(g_sddc, data, strlen(data));
    
    // 获取并打印网卡 mac 地址
    WiFi.macAddress(mac);
    sddc_printf("MAC addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
                mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]);
    // 使用网卡 mac 地址设置设备唯一标识 UID
    sddc_set_uid(g_sddc, mac);
}

void loop() {
  // 运行 SDDC 协议循环
    while (1) 
    {
        sddc_printf("SDDC running...\n");
        sddc_run(g_sddc);
        sddc_printf("SDDC quit!\n");
    }

    // 销毁 SDDC 协议
    sddc_destroy(g_sddc);
}
